package com.google.code.p.restrpc.client.serial;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.client.RestClientException;

import com.alibaba.fastjson.JSON;
import com.google.code.p.restrpc.DefaultRpcResponse;
import com.google.code.p.restrpc.RpcRequest;
import com.google.code.p.restrpc.TransferFormat;
import com.google.code.p.restrpc.http.ContentType;
import com.google.code.p.restrpc.http.DefaultResponseErrorHandler;
import com.google.code.p.restrpc.http.HttpClientException;
import com.google.code.p.restrpc.http.HttpClientNotFoundException;
import com.google.code.p.restrpc.http.HttpClientRefusedException;
import com.google.code.p.restrpc.http.HttpClientRequest;
import com.google.code.p.restrpc.http.HttpClientResponse;
import com.google.code.p.restrpc.http.HttpException;
import com.google.code.p.restrpc.http.HttpRequestCallback;
import com.google.code.p.restrpc.http.HttpResponseCallback;
import com.google.code.p.restrpc.http.HttpStatus;
import com.google.code.p.restrpc.http.ResponseErrorHandler;
import com.google.code.p.restrpc.http.entity.JsonEntity;

public class JsonSerializationProvider implements FormatSerializationProvider {
    
    private static final Log logger = LogFactory.getLog(JsonSerializationProvider.class);
    
    private HttpResponseCallback httpResponseCallback = new HttpResponseCallback() {
        
        public void handleError(HttpClientResponse response) {
            // TODO Auto-generated method stub
            
        }
        
        public Object doWith(HttpClientResponse response) {
            try {
                String responseStr = response.getBodyAsString();
                if (logger.isDebugEnabled()) {
                    logger.debug("Response Body:" + responseStr);
                }
                return JSON.parseObject(responseStr, DefaultRpcResponse.class);
            }
            catch (IOException e) {
                throw new HttpException(e);
            }
        }
    };
    
    private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler() {
        
        public void handleError(HttpClientResponse response) throws IOException {
            HttpStatus httpStatus = response.getStatusCode();
            String contentTypeStr = response.getHeaders().getContentType();
            Charset charset = null;
            byte[] body = null;
            if (StringUtils.isNotEmpty(contentTypeStr)) {
                ContentType contentType = ContentType.parse(contentTypeStr);
                charset = contentType != null ? contentType.getCharSet() : null;
                body = getResponseBody(response);
            }
            
            int statusSeriesCode = httpStatus.value() / 100;
            
            switch (httpStatus.value()) {
                case 403:
                    throw new HttpClientRefusedException(httpStatus,
                                                         body,
                                                         charset);
                case 404:
                    throw new HttpClientNotFoundException(httpStatus,
                                                          body,
                                                          charset);
            }
            
            switch (statusSeriesCode) {
                case 4:
                case 5:
                    throw new HttpClientException(httpStatus, body, charset);
                default:
                    throw new RestClientException("Unknown status code [" + httpStatus
                                                  + "]");
            }
        }
        
    };
    
    public TransferFormat getSupportedFormat() {
        return TransferFormat.JSON;
    }
    
    public HttpRequestCallback getHttpRequestCallback(final RpcRequest rpcRequest) {
        
        return new HttpRequestCallback() {
            
            public void doWith(HttpClientRequest request) {
                request.getHeaders()
                       .setUserAgent("Rest-Rpc 1.x (Over Jakarta HttpComponent Transport)");
                request.getHeaders().setContentType("text/json;charset=utf-8");
                request.getHeaders().setAccept("text/json;charset=utf-8");
                
                // TODO make this
                // configurable
                // request.getHeaders().setConnectionClose(true);
                
                if (null != rpcRequest.getData()) {
                    try {
                        request.setBody(new JsonEntity(rpcRequest.getData(),
                                                       "UTF-8"));
                    }
                    catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    
    public HttpResponseCallback getHttpResponseCallback() {
        return httpResponseCallback;
    }
    
    public ResponseErrorHandler getErrorHandler() {
        return errorHandler;
    }
    
}
